#ifndef _TABLESTRUCTURES_H_
#define _TABLESTRUCTURES_H_

#include <pthread.h>
#include "thing.h"
#include "table.h"

// A Set tracks and updates counts of hashkeys inside of it
// Every Set has a Thing name, letting multiple Sets use the same table

void InitTableStructures();

inline void TableInsertRemove(Table<Thing> * t, uint64 key, Thing value, 
			      int64 delta) {
  if (delta == 1) (*t)[key] = value;
  else t->Delete(key);
}

// Using tables to count things easily. For multiset counting operations.
inline 
int64 TableIntegerAdd(Table<Thing> * t, uint64 key, int64 delta) { 
  // if (delta) cout << "UpdateTable " << key << " " << delta << endl;
  Thing & v_ref = (*t)[key];
  int64 new_count = (v_ref.Is() ? v_ref.GetInteger() : 0) + delta;  
  if (new_count == 0) t->Delete(key);
  else v_ref = Thing::MakeInteger(new_count);
  return new_count;
}

inline
void TableIntegerSet(Table<Thing> * t, uint64 key, int64 value) {
  if (value == 0) t->Delete(key);
  else (*t)[key] = Thing::MakeInteger(value); }

inline int64 TableIntegerGet(Table<Thing> *t, uint64 key) {
  const Thing * look = (*t) % key;
  if (!look) return 0;
  return look->GetInteger();  
}

// Uses a Table to store a multiset of uint64s. 
// Each TableSet has a uint64 name.
BIND_HASH(kTableSetSize);
BIND_HASH(kTableSetWeight);
BIND_HASH(kTableSetPositionElement);
BIND_HASH(kTableSetElementPosition);
BIND_HASH(kTableSetSingular);

const int64 kSingularCount = 1;

struct TableSet {
  TableSet(Table<Thing>* t, uint64 name) : t_(t), name_(name) {}

  static TableSet Failure() { return TableSet(0,0); }
  bool Fail() const {  return (t_ == 0); }

  uint64 size() { 
    if (GetSingularValue().Is()) return 1; 
    return TableIntegerGet(t_, SetSizeKey());
  }
  uint64 weight() { 
    if (GetSingularValue().Is()) return kSingularCount;
    return TableIntegerGet(t_, SetWeightKey());
  } 

  void SetCount(Thing element, int64 new_val) {
    Update(element, new_val - Count(element));
  }

  void Update(Thing element, int64 delta) {
    if (delta == 0) return;
    // UpdateNoSingularValue(element, delta);
    // return;

    CHECK(element.Is());
    Thing singular_value = GetSingularValue();
    if (singular_value.Is()) {
      // cout << "-- singular value" << endl;
      t_->Delete(SingularKey());
      UpdateNoSingularValue(singular_value, kSingularCount);
      UpdateNoSingularValue(element, delta);
      // TODO: maybe be quicker about removing a singular value. 
      return;
    } 
    if ((delta == kSingularCount) && (size() == 0)) {
      (*t_)[SingularKey()] = element;
      old_count_ = 0;
      new_count_ = kSingularCount;
      old_size_ = 0;
      new_size_ = 1;
      old_weight_ = 0;
      new_weight_ = kSingularCount;
      return;
    }
    UpdateNoSingularValue(element, delta);
    if ((new_size_ == 1) && (new_weight_ == kSingularCount)) {
      Thing value = nth(0);
      uint64 old_size = old_size_;
      uint64 old_weight = old_weight_;
      uint64 old_count = old_count_;
      uint64 new_count = new_count_;
      UpdateNoSingularValue(value, -kSingularCount);
      (*t_)[SingularKey()] = value;
      old_size_ = old_size;
      old_weight_ = old_weight;
      old_count_ = old_count;
      new_size_ = 1;
      new_weight_ = kSingularCount;
      new_count_ = new_count;
      // that's all folks
    }
  }

  void UpdateNoSingularValue(Thing element, int64 delta) {
    CHECK(delta);
    uint64 epk = ElementPositionKey(element);
    // high bits of integer are count.  low bits are position. 
    int64 count_position = TableIntegerGet(t_, epk);
    old_count_ = count_position >> 32;    
    new_count_ = old_count_ + delta;
    if ( (new_count_ < 0) || (new_count_ >= (1ll << 28))) {
      cout << "element = " << ::ToString(element) << endl;
      cout << "delta = " << delta << endl;
      cout << "new_count_ = " << new_count_ << endl;
      CHECK(false);
    }
    // cout << "new_count_: " << new_count_ << endl;
    if (old_count_ && new_count_) {
      TableIntegerSet(t_, epk, count_position + (delta << 32));
      old_size_ = new_size_ = size();
    } else if (old_count_ == 0) {
      // add
      new_size_ = TableIntegerAdd(t_, SetSizeKey(), 1);
      old_size_ = new_size_ - 1;
      (*t_)[PositionElementKey(old_size_)] = element;
      TableIntegerSet(t_, epk, old_size_ + (delta << 32));
    } else {
      CHECK(new_count_ == 0);
      // Move the last element into the position of the deleted element. 
      uint64 position = count_position & 0xFFFFFFFFull; 
      new_size_ = TableIntegerAdd(t_, SetSizeKey(), -1);
      old_size_ = new_size_ + 1;
      Thing old_last_element = nth(new_size_);
      (*t_)[PositionElementKey(position)] = old_last_element;
      Thing & pos_ref = 
	(*t_)[ElementPositionKey(old_last_element)];
      pos_ref = 
	Thing::MakeInteger
	((pos_ref.GetInteger() & 0xFFFFFFFF00000000ull) + position);
      t_->Delete(PositionElementKey(new_size_));
      t_->Delete(ElementPositionKey(element));
    }
    Thing & weight_ref = (*t_)[SetWeightKey()];
    old_weight_ = weight_ref.GetInteger();
    new_weight_ = old_weight_ + delta;
    weight_ref = Thing::MakeInteger(new_weight_);

    if (new_size_ == 0) {
      CHECK(new_weight_ == 0);
      t_->Delete(SetSizeKey());
      t_->Delete(SetWeightKey());
    }
  }

  uint64 Count(Thing element) {
    if (GetSingularValue() == element) return kSingularCount;
    const Thing * look = (*t_) % ElementPositionKey(element);
    if (!look) return 0;
    return look->GetInteger() >> 32;
  }

  Thing nth(uint64 n, bool fallable = false) { 
    if (n == 0) {
      Thing singular_value = GetSingularValue();
      if (singular_value.Is()) return singular_value;
    }
    Thing ret = t_->GetValue(PositionElementKey(n)); 
    CHECK(ret.Is() || fallable);
    return ret;
  }
  Thing fallable_nth(uint64 n) { 
    return nth(n, true);
  }

  string ToString() {
    return "not implemented";
    /*
    ostringstream s;
    uint64 n = size(); s << "{ ";
    for (uint64 i=0; i<n; i++) 
      s << "(" << nth(i) << ", " << Count(nth(i)) << ") "; s << "} " << endl;
    cout << "count " << old_count_ << " -> " << new_count_ << endl
	 << "size  " << old_size_ << " -> " << new_size_ << endl
	 << "weight  " << old_weight_ << " -> " << new_weight_ << endl;
    return s.str(); 
    */
  }

  // Private
  uint64 SetSizeKey() { return Fingerprint(name_, kTableSetSize); }
  uint64 SingularKey() { return Fingerprint(name_, kTableSetSingular); }
  uint64 SetWeightKey() { return Fingerprint(name_, kTableSetWeight); }
  uint64 PositionElementKey(uint64 position) {
    return Fingerprint(name_, position, kTableSetPositionElement); }
  uint64 ElementPositionKey(Thing element) {
    return Fingerprint(name_, element.ID(), kTableSetElementPosition); }
  
  Thing GetSingularValue() { return t_->GetValue(SingularKey()); }
  
  Table<Thing> * t_;
  uint64 name_;

  // Temporary space to pass back info on changes. 
  int64 old_count_, new_count_, old_size_, new_size_, old_weight_, new_weight_;
};

int TableSetTest();

#endif
